package com.ams.service.impl;

import bcssg.wdssvr.webservice.WdcsService;
import bcssg.wdssvr.webservice.WdcsServiceImplService;
import cn.baiy.byConfig;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ams.common.entity.*;
import com.ams.common.utils.RedisKeys;
import com.ams.common.utils.RedisUtils;
import com.ams.common.utils.Retrys;
import com.ams.enums.LightOperationStatus;
import com.ams.enums.LinkOperationStatus;
import com.ams.enums.LinkResultStatus;
import com.ams.model.InvLotLocId;
import com.ams.service.BasLocationService;
import com.ams.service.InvLotLocIdService;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URISyntaxException;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;

@Service("wcsService")
@Slf4j
public class WcsService {

    final String ESCOOKIE = "ESCOOKIE";
    final String ESSID = "ESSID";
    final long ESCOOKIE_EXPIRE = 60 * 20;
	@Autowired
    RestTemplate directRestTemplate;
	@Autowired
    RestTemplate normalRestTemplate;
    @Autowired
    RedisUtils redisUtil;
    @Autowired
    BasLocationService basLocationService;
    @Autowired
    InvLotLocIdService invLotLocIdService;
    @Value("${ams.link.url}")
    String linkUrl;
    @Value("${ams.es.url}")
    String baseUrl;
    @Value("${ams.es.pwd}")
    String esPwd;
    @Value("${ams.light.url}")
    String lightUrl;
	String doorUrl = "http://222.73.210.141:8121";
	String XUser = "admin";

    public static void main(String[] args) {
////	    System.out.print(DateUtil.format(new Date(), "yyyyMMddHHmmss"));
////        System.out.print(RandomUtil.randomNumbers(6));
////        sid:c2dbfe9eb16e7e1d0ad117fc96cb7c0b
////
////        an:53c5a5772a11b21f785ec3f6374a47554fe5c2d4
////
////        pwd:WNxIpwysVlrLFR8mqHwal7Twyl7uO
//
//        ScriptEngineManager sem=new ScriptEngineManager();
//        ScriptEngine se=sem.getEngineByExtension("js");
//        try {
//            se.eval(new FileReader(new File("").getCanonicalPath() + "/baiy.js"));
//        } catch (ScriptException e) {
//            e.printStackTrace();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//        String ppp = (String) se.eval("eval(\"BAIY.CRYPTO.MAC.HexHmacSha1(BAIY.CRYPTO.HASH.BlobSha1('IoT-ES:WxNxIpwysVlrLFR8mqHwal7Twyl7uO'), '" + sid + "')\")");

    }

	public String login() {
        String escookie = (String)redisUtil.get(ESCOOKIE);
        String essid = (String)redisUtil.get(ESSID);
//        log.info("【login 开始】escookie = {} essid = {}", escookie, essid);
        try {
            if(escookie == null ) {
                LoginBegin loginBegin = new LoginBegin().invoke();
                String sid = loginBegin.getSid();
                String cookie = loginBegin.getCookie();
                String answerCode = getAnswerCode(sid);
                boolean result = requestLoginAnswer(cookie, answerCode, false);
//                log.info("【login answer1】result = {}", result);
            } else {
//                log.info("【login cache】escookie = {}", escookie);
            }
            escookie = (String)redisUtil.get(ESCOOKIE);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("WcsService 登录异常", e);
            return null;
        }
//        log.info("【login escookie】escookie = {}", escookie);
		return escookie;
	}

    private boolean requestLoginAnswer(String cookie, String answerCode, boolean retry) throws URISyntaxException, IOException, ScriptException {
        HttpHeaders headers = new HttpHeaders();
        headers.add("Cookie", cookie);
        headers.add("X-User", XUser);
        headers.add("X-Answer", answerCode);
        String bodyStr = "";
//        SimpleClientHttpRequestFactory reqfac = new SimpleClientHttpRequestFactory();
//        reqfac.setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("192.168.2.99", 8888)));
//        directRestTemplate.setRequestFactory(reqfac);
        HttpEntity<String> formEntity = new HttpEntity<String>(bodyStr, headers);
        ResponseEntity<String> response = directRestTemplate.postForEntity(baseUrl + "/BFC/Auth/LoginAnswer", formEntity, String.class);
        if (response.getStatusCode() == HttpStatus.OK) {
            return true;
        } else if(response.getStatusCode() == HttpStatus.UNAUTHORIZED && retry == false) {
            LoginBegin loginBegin = new LoginBegin().invoke();
            String sid = loginBegin.getSid();
            cookie = loginBegin.getCookie();
            answerCode = getAnswerCode(sid);
            return requestLoginAnswer(cookie, answerCode, true);
        }
        return false;
    }

    private String getAnswerCode(String sid) throws ScriptException, IOException {
        ScriptEngineManager sem=new ScriptEngineManager();
        ScriptEngine se=sem.getEngineByExtension("js");
        se.eval(new FileReader(new File("").getCanonicalPath() + "/baiy.js"));
        return (String) se.eval("eval(\"BAIY.CRYPTO.MAC.HexHmacSha1(BAIY.CRYPTO.HASH.BlobSha1('IoT-ES:" + esPwd + "'), '" + sid + "')\")");
    }

    public void clearLoginCache() {
        redisUtil.delete(ESCOOKIE);
        redisUtil.delete(ESSID);
    }

    public List<StorageRack> parseRackData(String data) {
        List<List> rackStrList = JSONArray.parseArray(data, List.class);
        List<StorageRack> storageRackList = new ArrayList<>();
        for (List list : rackStrList) {
            StorageRack storageRack = new StorageRack();
            storageRack.setRackId((String) list.get(1));
            storageRackList.add(storageRack);
        }

        return storageRackList;
    }

    public List<StorageLocation> parseMatDetailData(String data) {
        List<List> rackStrList = JSONArray.parseArray(data, List.class);
        Map<String, StorageLocation> storageLocationMap = new HashMap<>();
        for (List list : rackStrList) {
            String[] keyArr = ((String) list.get(0)).split("\\\\");
            if(keyArr.length < 2) {
                continue;
            }
            String locationId = keyArr[1];
            String attr = ((String) list.get(1));
            Object value = list.get(3);
            StorageLocation storageLocation = this.getStorageLocation(storageLocationMap, locationId);
            storageLocation.setLocationId(locationId);
            switch (attr) {
                case "Expected":
                    if(value.toString().indexOf("429496") > -1) {
                        storageLocation.setExpected(-1);
                    } else {
                        storageLocation.setExpected(Integer.parseInt(value.toString()));
                    }
                    break;
                case "Justness":
                    if(value.toString().indexOf("429496") > -1) {
                        storageLocation.setJustness(-1d);
                    } else {
                        storageLocation.setJustness(Double.parseDouble(value.toString()));
                    }
                    break;
                case "Weight":
                    if(value.toString().indexOf("429496") > -1) {
                        storageLocation.setWeight(-1d);
                    } else {
                        storageLocation.setWeight(Double.parseDouble((value.toString())));
                    }
                    break;
                case "Raw ADC":
                    if(value.toString().indexOf("429496") > -1) {
                        storageLocation.setWeight(-1d);
                    } else {
                        storageLocation.setRawAdc(Double.parseDouble(value.toString()));
                    }
                case "Status":
                    if(value.toString().indexOf("429496") > -1) {
                        storageLocation.setStatus(-1d);
                    } else {
                        storageLocation.setStatus(Double.parseDouble(value.toString()));
                    }
                    break;
                case "Quantity":
                    if(value.toString().indexOf("429496") > -1) {
                        storageLocation.setQuantity(BigDecimal.valueOf(-1));
                    } else {
                        storageLocation.setQuantity(BigDecimal.valueOf(Double.parseDouble(value.toString())));
                    }
                    break;
                case "Lights":
                    if(!value.toString().equals("4294967295")) {
                        storageLocation.setLights(Double.parseDouble(value.toString()));
                    } else {
                        storageLocation.setLights(-1d);
                    }
                    break;
            }

            storageLocationMap.put(locationId, storageLocation);

        }

        List<StorageLocation> storageLocationList = new ArrayList<>();
        for (Map.Entry<String, StorageLocation> entry : storageLocationMap.entrySet()) {
            storageLocationList.add(entry.getValue());
        }
        return storageLocationList;
    }

    public StorageLocation getStorageLocation(Map<String, StorageLocation> storageLocationMap, String locationId) {
	    if(storageLocationMap.containsKey(locationId)) {
            return storageLocationMap.get(locationId);
        } else {
            StorageLocation storageLocation = new StorageLocation();
            storageLocationMap.put(locationId, storageLocation);
            return storageLocation;
        }
    }

    public boolean initPreAuthDelta() {
        try {
            List<StorageRack> storageRackList = this.getRetriveList(false);
            if (storageRackList != null) {
                for (StorageRack storageRack : storageRackList) {
                    List<PreAuthDeltaItem> preAuthDeltaItemList = new ArrayList<>();
                    for (StorageLocation storageLocation : storageRack.getStorageLocationList()) {
                        PreAuthDeltaItem preAuthDeltaItem = new PreAuthDeltaItem();
                        preAuthDeltaItem.setFrom(0);
                        preAuthDeltaItem.setTo(0);
                        preAuthDeltaItem.setNumber(storageLocation.getLocationId());
                        preAuthDeltaItemList.add(preAuthDeltaItem);

                        boolean succ = setPreAuthDelta(storageRack.getRackId(), storageLocation.getLocationId(), BigDecimal.ZERO, BigDecimal.ZERO);
                        if (!succ) {
                            return false;
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("【setPreAuthDelta 出错】", e);
            return false;
        }
        return true;
    }

    public boolean setPreAuthDelta(String rackId, String locationId, BigDecimal from , BigDecimal to) {
        String cookie = login();
        HttpHeaders headers = new HttpHeaders();
        headers.add("Cookie", cookie);
        headers.add("Content-Type", "application/json");
        headers.add("Accept", "application/json");

        String args = "[\"Args\",\"Number\",\"STRING\",\"" + locationId + "\"],[\"Args\",\"From\",\"DWORD\",\"" + from.intValue() + "\"],[\"Args\",\"To\",\"DWORD\",\"" + to.intValue() + "\"]";
        String bodyStr = "[[\"\",\"Rack\",\"STRING\",\"" + rackId + "\"], [\"\",\"api\",\"STRING\",\"Pallet/SetPreAuthDelta\"],[\"Args\",\"\",\"KEY\",\"\"]," + args + "]";
        HttpEntity<String> formEntity = new HttpEntity<String>(bodyStr, headers);
        ResponseEntity<String> matResponse = directRestTemplate.postForEntity(baseUrl + "/BFC/Rack/CallNode", formEntity, String.class);
        if (matResponse.getStatusCode() == HttpStatus.OK) {
            return true;
        } else {
            log.error("【setPreAuthDelta getStatusCode error】status code = " + matResponse.getStatusCode());
            return false;
        }
    }

    public String lpWaitMessage() {
        String cookie = login();
        if (cookie == null) {
            log.error("【getRetriveList】 error cookie is null >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
            return null;
        }

        HttpHeaders headers = new HttpHeaders();
        headers.add("Cookie", cookie);
        headers.add("Content-Type", "application/json");
        headers.add("Accept", "application/json");
        ResponseEntity<String> response = directRestTemplate.exchange(
                baseUrl + "/BFC/App/lpWaitMessage",
                HttpMethod.GET,
                new HttpEntity<String>(headers),
                String.class);
        if (response.getStatusCode() == HttpStatus.OK) {
            return response.getBody();
        }else{
            return null;
        }
    }

    public List<StorageRack> getRetriveList(boolean isRetry) throws IOException, URISyntaxException {
//        RestTemplate restTemplate = new RestTemplate();
        String cookie = login();
        if (cookie == null) {
            log.error("【getRetriveList】 error cookie is null >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
            return null;
        }
        HttpHeaders headers = new HttpHeaders();
        headers.add("Cookie", cookie);
        headers.add("Content-Type", "application/json;chatset=utf-8");
        headers.add("Accept", "application/json;chatset=utf-8");
        headers.add("Accept-Encoding", "");
        ResponseEntity<String> response = directRestTemplate.exchange(
                baseUrl + "/BFC/Rack/RetriveList",
                HttpMethod.GET,
                new HttpEntity<String>(headers),
                String.class);
        if (response.getStatusCode() == HttpStatus.OK) {
            log.info("【getRetriveList】 >>>>>>>>>>>>>>>> {}", response.getBody());
            List<StorageRack> storageRackList = this.parseRackData(response.getBody());
            for (StorageRack storageRack : storageRackList) {
                String bodyStr = "[[\"\",\"Rack\",\"STRING\",\"" + storageRack.getRackId() + "\"], [\"\",\"api\",\"STRING\",\"Pallet/RetriveStatusList\"]]";
                HttpEntity<String> formEntity = new HttpEntity<String>(bodyStr, headers);
//                ResponseEntity<String> matResponse = directRestTemplate.postForEntity(baseUrl + "/BFC/Rack/CallNode", formEntity, String.class);
                ResponseEntity<String> matResponse = directRestTemplate.exchange(baseUrl + "/BFC/Rack/CallNode", HttpMethod.POST, formEntity, String.class);
                List<StorageLocation> storageLocationList = this.parseMatDetailData(matResponse.getBody());
                storageRack.setStorageLocationList(storageLocationList);
            }
            return storageRackList;
        } else if(response.getStatusCode() == HttpStatus.UNAUTHORIZED && !isRetry){
            this.clearLoginCache();
            return this.getRetriveList(true);
        }else {
            log.error("【getRetriveList】 error getStatusCode {}", response.getStatusCode().value());
            return null;
        }
    }

    public Boolean openDoor() {
        HttpHeaders headers = new HttpHeaders();
        String bodyStr = "{\n" + "\"duration\":\"10\"\n" + "}";
        HttpEntity<String> formEntity = new HttpEntity<String>(bodyStr, headers);
        ResponseEntity<String> response = directRestTemplate.postForEntity(doorUrl +"/ams/api/opendoor", formEntity, String.class);
        if (response.getStatusCode() == HttpStatus.OK) {
            if(response.getBody().indexOf("success") > -1) {
                return true;
            } else {
                log.error("【openDoor error】>>>>>>>>>>>{}", response.getBody());
                return false;
            }

        } else {
            log.error("【openDoor error】>>>>>>>>>>>{}", response.getBody());
            return false;
        }
    }

    public Boolean initStock() {
        try {
            List<StorageRack> storageRackList = this.getRetriveList(false);
            for (StorageRack storageRack : storageRackList) {
                for (StorageLocation storageLocation : storageRack.getStorageLocationList()) {
                    InvLotLocId invLotLocId = new InvLotLocId();
                    invLotLocId.setLotnum("");
                    String matno = basLocationService.getMatnoByRackIdAndLotId(storageRack.getRackId(), storageLocation.getLocationId());
                    invLotLocId.setSku(matno);
                    invLotLocId.setLocationid(storageLocation.getLocationId());
                    invLotLocId.setRackid(storageRack.getRackId());
                    invLotLocId.setCustomerid("WRC");
                    List<InvLotLocId> invLotLocIds = invLotLocIdService.findByModel(invLotLocId);
                    invLotLocId.setQty(storageLocation.getQuantity());
                    invLotLocId.setQtyallocated(BigDecimal.ZERO);
                    invLotLocId.setCreateTime(new Date());
                    invLotLocId.setUpdateTime(new Date());
                    if(invLotLocIds.size() > 0) {
                        invLotLocIdService.update(invLotLocId);
                    }else {
                        invLotLocIdService.insert(invLotLocId);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("【initStock error】初始化库存失败",e);
            return false;
        }

        return true;
    }

    public Boolean linkOperation(String trayId, Integer status) {
        ResponseEntity<String> response = null;
        if (status == LinkOperationStatus.CALL.getCode()) {
            String json = "{\"Key\":\"\",\"SynchronizedWait\":10,\"PrefixId\":1,\"PrefixPort\":1,\"argu\":[" + trayId + ",1],\"Op\":2}";
            response = directRestTemplate.getForEntity(linkUrl + "/wms_operation?data={json}", String.class, json);
            redisUtil.set(RedisKeys.LINK_TRAY_ID, trayId);
        } else if(status == LinkOperationStatus.RETURN.getCode()) {
            String json = "{\"Key\":\"\",\"SynchronizedWait\":10,\"PrefixId\":1,\"PrefixPort\":1,\"argu\":[1],\"Op\":3}";
            response = directRestTemplate.getForEntity(linkUrl + "/wms_operation?data={json}", String.class, json);
            redisUtil.delete(RedisKeys.LINK_TRAY_ID);
        }

        if (response.getStatusCode() == HttpStatus.OK) {
            LinkResult linkResult = JSONObject.parseObject(response.getBody(), LinkResult.class);
            if (linkResult.getResult().size() > 0) {
                Integer optStatus = linkResult.getResult().get(0);
//                if (optStatus != LinkResultStatus.LOCATION_TRAY_EXSITS.getCode()) {
//                    try {
//                        Thread.sleep(2000);
//                        //超时重发
//                        this.linkOperation(trayId, status);
//                    } catch (InterruptedException e) {
//                        e.printStackTrace();
//                    }
//                } else {
//                    log.error("【linkOperation error】>>>>>>>> status = " + optStatus);
//                }
            } else {
                try {
                    Thread.sleep(2000);
                    //超时重发
                    this.linkOperation(trayId, status);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return true;
        }else{
            return false;
        }
    }

    public Boolean lightOperation(LightOperationStatus status){
        boolean optSucc = true;
        try {
            log.info("【lightOperation status change】status = {}", status);
            switch (status) {
                case NORMAL:
                    optSucc = requestLight(LightOperationStatus.RESET.getMessage());
                    if(optSucc) {
                        optSucc = requestLight(LightOperationStatus.NORMAL.getMessage());
                    }
                    break;
                case PART_FINISH:
                    optSucc = requestLight(LightOperationStatus.RESET.getMessage());
                    if(optSucc) {
                        optSucc = requestLight(LightOperationStatus.PART_FINISH.getMessage());
                        if(!optSucc) {
                            break;
                        }
                        Thread.sleep(2000);
                        optSucc = requestLight(LightOperationStatus.RESET.getMessage());
                        if(!optSucc) {
                            break;
                        }
                        optSucc = requestLight(LightOperationStatus.NORMAL.getMessage());
                    }
                    break;

                case CHANGE:
                    optSucc = requestLight(LightOperationStatus.RESET.getMessage());
                    if(optSucc) {
                        requestLight(LightOperationStatus.CHANGE.getMessage());
                    }
                    break;

                case ALL_FINISH:
                    optSucc = requestLight(LightOperationStatus.RESET.getMessage());
                    if(optSucc) {
                        optSucc = requestLight(LightOperationStatus.ALL_FINISH.getMessage());
                    }
                    break;

                case MIS:
                    optSucc = requestLight(LightOperationStatus.RESET.getMessage());
                    if(optSucc) {
                        optSucc = requestLight(LightOperationStatus.MIS.getMessage());
                    }

                    break;

                case EQUIPMENT_MIS:
                    optSucc = requestLight(LightOperationStatus.RESET.getMessage());
                    if(optSucc) {
                        optSucc = requestLight(LightOperationStatus.EQUIPMENT_MIS.getMessage());
                    }

                    break;

                case EQUIPMENT_NOR:
                    optSucc = requestLight(LightOperationStatus.RESET.getMessage());
                    if(optSucc) {
                        optSucc = requestLight(LightOperationStatus.EQUIPMENT_NOR.getMessage());
                    }
                    break;

                case EQUIPMENT_WARN:
                    optSucc = requestLight(LightOperationStatus.RESET.getMessage());
                    if(optSucc) {
                        optSucc = requestLight(LightOperationStatus.EQUIPMENT_WARN.getMessage());
                    }
                    break;
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        return optSucc;
    }

    private Boolean requestLight(String lightReqName) throws Exception {
        HttpHeaders headers = new HttpHeaders();
        String bodyStr = "";
        HttpEntity<String> formEntity = new HttpEntity<String>(bodyStr, headers);
        ResponseEntity<String> response = Retrys.retry(() -> {
            ResponseEntity<String> _response = directRestTemplate.postForEntity(lightUrl + "/api/smart/" + lightReqName, formEntity, String.class);
            return _response;
        });

        if (response.getStatusCode() == HttpStatus.OK) {
            return true;
        } else {
            log.info("【lightOperation error】body >>>>>>>>>>>>>" + response.getBody());
            return false;
        }
    }

    private class LoginBegin {
        private String sid;
        private String cookie;

        public String getSid() {
            return sid;
        }

        public String getCookie() {
            return cookie;
        }

        public LoginBegin invoke() throws IOException, URISyntaxException {
            ResponseEntity<String> response = directRestTemplate.getForEntity(baseUrl + "/BFC/Auth/LoginBegin", String.class);
            sid = "";
            cookie = "";
            if (response.getStatusCode() == HttpStatus.OK) {
                HttpHeaders httpHeaders = response.getHeaders();
                sid = httpHeaders.get("X-SID").get(0);
                cookie = httpHeaders.get("Set-Cookie").get(0);
                redisUtil.set(ESCOOKIE, cookie, ESCOOKIE_EXPIRE);
                redisUtil.set(ESSID, sid, ESCOOKIE_EXPIRE);
            }
            return this;
        }
    }

    public boolean finishWms1TaskJson(String data) {
        WdcsServiceImplService wdcsServiceImplService = new WdcsServiceImplService();
        WdcsService wdcsService = wdcsServiceImplService.getWdcsServiceImplPort();
        String res = wdcsService.finishWms1TaskJson(data);
        if (StringUtils.isBlank(res)) {
            return false;
        }

        DataInsEntity dataInsEntity = JSONObject.parseObject(res, DataInsEntity.class);
        if (dataInsEntity != null && dataInsEntity.getData().size() > 0) {
            return true;
        }

        return false;
    }

    public boolean sendDeviceStatusJson(String data) {
        WdcsServiceImplService wdcsServiceImplService = new WdcsServiceImplService();
        WdcsService wdcsService = wdcsServiceImplService.getWdcsServiceImplPort();
        String res = wdcsService.sendDeviceStatusJson(data);
        if (StringUtils.isBlank(res)) {
            return false;
        }

        DataInsEntity dataInsEntity = JSONObject.parseObject(res, DataInsEntity.class);
        if (dataInsEntity != null && dataInsEntity.getData().size() > 0) {
            return true;
        }

        return false;
    }

    @Async
    public void asyncFinishWms1TaskJson(String finishJson) {
        try{
            boolean wdsSucc = this.finishWms1TaskJson(finishJson);
            if(!wdsSucc) {
                log.info("【WDS 通知失败 asn data = {}", finishJson);
            }else{
                log.info("【WDS 通知成功过】 >>>>>>>>>>>>>>>>>>>>>>>>>");
            }
        } catch (Exception e) {
            log.error("【WDS finishWms1TaskJson erro  】>>>>>>>>>>>>>>>>>>>>>> ", e);
        }

    }
}
